/*
Copyright (c) 2025 HeZongLun
NahidaProject is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan
PSL v2.
You may obtain a copy of Mulan PSL v2 at:
		 http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
*/

#include "SHA256.h"

void NahidaProject::SHA256::Initial() {
	h[0] = 0x6a09e667;
	h[1] = 0xbb67ae85;
	h[2] = 0x3c6ef372;
	h[3] = 0xa54ff53a;
	h[4] = 0x510e527f;
	h[5] = 0x9b05688c;
	h[6] = 0x1f83d9ab;
	h[7] = 0x5be0cd19;
}

void NahidaProject::SHA256::ProcessBlock(const uint8_t* block) {
	uint32_t w[64];

	for (int i = 0; i < 16; i++) {
		w[i] = (static_cast<uint32_t>(block[i * 4]) << 24) | (static_cast<uint32_t>(block[i * 4 + 1]) << 16) | (static_cast<uint32_t>(block[i * 4 + 2]) << 8) | (static_cast<uint32_t>(block[i * 4 + 3]));
	}

	for (int i = 16; i < 64; i++) {
		w[i] = Gamma1(w[i - 2]) + w[i - 7] + Gamma0(w[i - 15]) + w[i - 16];
	}

	uint32_t a = h[0];
	uint32_t b = h[1];
	uint32_t c = h[2];
	uint32_t d = h[3];
	uint32_t e = h[4];
	uint32_t f = h[5];
	uint32_t g = h[6];
	uint32_t hh = h[7];

	for (int i = 0; i < 64; i++) {
		uint32_t t1 = hh + Sigma1(e) + Ch(e, f, g) + sha256K[i] + w[i];
		uint32_t t2 = Sigma0(a) + Maj(a, b, c);

		hh = g;
		g = f;
		f = e;
		e = d + t1;
		d = c;
		c = b;
		b = a;
		a = t1 + t2;
	}

	h[0] += a;
	h[1] += b;
	h[2] += c;
	h[3] += d;
	h[4] += e;
	h[5] += f;
	h[6] += g;
	h[7] += hh;
}

void NahidaProject::SHA256::ProcessFinalBlock(const uint8_t* data, size_t length, uint64_t totalBits) {
	uint8_t block[64];

	memset(block, 0, sizeof(block));
	memcpy(block, data, length);

	block[length] = 0x80;

	if (length >= 56) {
		ProcessBlock(block);
		memset(block, 0, sizeof(block));
	}

	uint64_t bitLength = totalBits;
	for (int i = 0; i < 8; i++) {
		block[63 - i] = static_cast<uint8_t>(bitLength >> (i * 8));
	}

	ProcessBlock(block);
}

std::vector<uint8_t> NahidaProject::SHA256::Hash(const std::string& input) {
	Initial();

	const uint8_t* data = reinterpret_cast<const uint8_t*>(input.c_str());
	size_t length = input.length();
	uint64_t totalBits = length * 8;

	size_t fullBlocks = length / 64;
	for (size_t i = 0; i < fullBlocks; i++) {
		ProcessBlock(data + i * 64);
	}

	size_t remaining = length % 64;
	ProcessFinalBlock(data + fullBlocks * 64, remaining, totalBits);

	std::vector<uint8_t> result(32);
	for (int i = 0; i < 8; i++) {
		result[i * 4] = static_cast<uint8_t>(h[i] >> 24);
		result[i * 4 + 1] = static_cast<uint8_t>(h[i] >> 16);
		result[i * 4 + 2] = static_cast<uint8_t>(h[i] >> 8);
		result[i * 4 + 3] = static_cast<uint8_t>(h[i]);
	}

	return result;
}

std::vector<uint8_t> NahidaProject::SHA256::Hash(const std::vector<uint8_t>& input) {
	Initial();

	const uint8_t* data = input.data();
	size_t length = input.size();
	uint64_t totalBits = length * 8;

	size_t fullBlocks = length / 64;
	for (size_t i = 0; i < fullBlocks; i++) {
		ProcessBlock(data + i * 64);
	}

	size_t remaining = length % 64;
	ProcessFinalBlock(data + fullBlocks * 64, remaining, totalBits);

	std::vector<uint8_t> result(32);
	for (int i = 0; i < 8; i++) {
		result[i * 4] = static_cast<uint8_t>(h[i] >> 24);
		result[i * 4 + 1] = static_cast<uint8_t>(h[i] >> 16);
		result[i * 4 + 2] = static_cast<uint8_t>(h[i] >> 8);
		result[i * 4 + 3] = static_cast<uint8_t>(h[i]);
	}

	return result;
}

std::string NahidaProject::SHA256::BytesToHex(const std::vector<uint8_t>& bytes) {
	std::stringstream ss;
	ss << std::hex << std::setfill('0');
	for (uint8_t byte : bytes) {
		ss << std::setw(2) << static_cast<int>(byte);
	}
	return ss.str();
}

std::string NahidaProject::SHA256::HashToString(const std::string& input) {
	auto hashBytes = Hash(input);
	return BytesToHex(hashBytes);
}